//
//  NSObject+Serializable.m
//  SwiftDemo
//
//  Created by Yuan on 2017/5/13.
//  Copyright © 2017年 YuanLiang's MacBook. All rights reserved.
//

#import "NSObject+Serializable.h"
#import <objc/runtime.h>
#import "NSObjectSafe.h"

@implementation NSObject (Serializable)

- (NSArray *)parseJsonArray:(NSArray *)jsonArray forClass:(Class)clazz {

    NSMutableArray *arrayDest = [[NSMutableArray alloc]init];
    // 基本数据类型
    if([clazz isSubclassOfClass:[NSString class]] || [clazz isSubclassOfClass:[NSNumber class]] || !clazz) {
        [arrayDest addObjectsFromArray:jsonArray];
    } else {
        // 解析
        NSInteger jsonCount = [jsonArray count];
        for(NSInteger i = 0; i < jsonCount; i++) {
            id jsonValueObject = [jsonArray objectAtIndex:i];

            if ([jsonValueObject isKindOfClass:[NSArray class]] || [jsonValueObject isKindOfClass:[NSMutableArray class]]) {
                //嵌套数组 递归
                NSArray *arrayObject = [self parseJsonArray:jsonValueObject forClass:clazz];
                [arrayDest addObject:arrayObject];
                
            } else if([jsonValueObject isKindOfClass:[NSDictionary class]]) {
                // 字典类型，直接赋值
                if ([clazz isSubclassOfClass:[NSDictionary class]]) {
                    NSMutableDictionary *varObject = [[NSMutableDictionary alloc] initWithDictionary:jsonValueObject];
                    
                    [arrayDest addObject:varObject];
                } else {
                    //自定义对象
                    id varObject = [[clazz alloc] init];
                    
                    // 进行自定义解析
                    if ([varObject respondsToSelector:@selector(parseJsonObjectManual:)]) {
                        [varObject parseJsonObjectManual:jsonValueObject];
                    }
                    // 递归进行下层解析
                    else {
                        [varObject parseJsonObject:jsonValueObject];
                    }
                    
                    [arrayDest addObject:varObject];
                    
                }
            }
        
        }
    }
    return arrayDest;
}


- (void)parseJsonObject:(id)jsonObject{
    NSDictionary *jsonDictionary = [jsonObject json2Dictionary];
    if (!jsonDictionary) {
        //NSAssert(NO, @"parseJsonObject: Object is not Json");
        return;
    }
    unsigned int count = 0;
    objc_property_t *props = class_copyPropertyList([self class], &count);
    NSArray<NSString *> *ignores = nil;
    NSDictionary<NSString *,NSString *> *renameMapping = nil;
    if ([self conformsToProtocol:@protocol(TJSerializableProtocol)]) {
        if ([self respondsToSelector:@selector(propertiesIgnoreInSerialize)])
            ignores = [(id<TJSerializableProtocol>)self propertiesIgnoreInSerialize];
        if ([self respondsToSelector:@selector(propertiesDeserializeNameMapping)])
            renameMapping = [(id<TJSerializableProtocol>)self propertiesDeserializeNameMapping];
    }
    
    for (int i = 0; i < count; i++) {
        
        objc_property_t prop = props[i];
        
        //属性名
        const char *cName = property_getName(prop);
        NSString *propName = [NSString stringWithUTF8String:cName];
        if ([[self class] isNSObjectProperty:propName]){
            //过滤NSObject属性
            continue;
        }
        
        //反序列化乎略
        if (ignores) {
            BOOL matched = NO;
            for (NSString *ignore in ignores) {
                if ([ignore isEqualToString:propName]) {
                    matched = YES;
                    break;
                }
            }
            if (matched) continue; // go to next property.
        }
        
        // rename
        NSString *propNameDeserialize = [renameMapping objectForKey:propName];
        if (!propNameDeserialize) propNameDeserialize = propName;
        //属性描述
        const char *cAttr = property_getAttributes(prop);
        NSString *propertyType = [NSString stringWithUTF8String:cAttr];
//        NSLog(@"%@", propertyType);
//        Class clazz = [self queryClassWithAttribute:propertyType];
//        if ([propName isEqualToString:@"wayId"]) {
//            NSInteger hit = 0;
//        }
        
        //对应属性名的Key值
        id jsonValue = [jsonDictionary objectForKey:propNameDeserialize];
 
        if ([propertyType hasPrefix:@"T@\"NSString\""] || [propertyType hasPrefix:@"T@\"NSMutableString\""]) {
            if (jsonValue != nil && ([jsonValue isKindOfClass:[NSString class]] || [jsonValue isKindOfClass:[NSMutableString class]])) {
                [self setValue:jsonValue forKey:propName];
            }
        }
        /**
         所有简单类型都会被处理成Number
         Swift.Int: Tq, Swift.Float: Tf, Swift.Double: Td, Swift.Bool: TB
         NSInteger: Tq, CGFloat: Td, uint:TI, int: Ti, BOOL: TB, NSUInteger: TQ, long: Tl
         */
        else if ([propertyType hasPrefix:@"T@\"NSNumber\""] || [propertyType hasPrefix:@"Tq"] || [propertyType hasPrefix:@"TQ"] || [propertyType hasPrefix:@"Td"] || [propertyType hasPrefix:@"TI"] || [propertyType hasPrefix:@"Ti"] || [propertyType hasPrefix:@"TB"] || [propertyType hasPrefix:@"Tf"] || [propertyType hasPrefix:@"Tl"]) {
            if (jsonValue != nil && [jsonValue isKindOfClass:[NSNumber class]]) {
                [self setValue:jsonValue forKey:propName];
            }
        }
        else if ([propertyType hasPrefix:@"T@\"NSArray"] || [propertyType hasPrefix:@"T@\"NSMutableArray"]) {
            if (jsonValue && ([jsonValue isKindOfClass:[NSArray class]] || [jsonValue isKindOfClass:[NSMutableArray class]])){
                Class clazz = nil;
                if ([self respondsToSelector:@selector(serializableTypeInArray:)]) {
                    clazz = [((id <TJSerializableProtocol>) self) serializableTypeInArray: propName];
                }
                NSArray *arrayDest = [self parseJsonArray:jsonValue forClass:clazz];
                [self setValue:arrayDest forKey:propName];
            }
        }
        else if ([propertyType hasPrefix:@"T@\"NSDictionary\""] || [propertyType hasPrefix:@"T@\"NSMutableDictionary\""]) {
            if (jsonValue != nil && ([jsonValue isKindOfClass:[NSDictionary class]] || [jsonValue isKindOfClass:[NSMutableDictionary class]]))
            {
                // 赋值
                [self setValue:jsonValue forKey:propName];
            }
            
        }
        else if ([propertyType hasPrefix:@"Tc"] && [propertyType hasSuffix:propName] && [jsonValue isKindOfClass:NSNumber.class]) {
            // OC - BOOL
            [self setValue:jsonValue forKey:propName];
        }
        else if ([propertyType hasPrefix:@"T@\""]) {
            if (jsonValue != nil && ([jsonValue isKindOfClass:[NSDictionary class]] || [jsonValue isKindOfClass:[NSMutableDictionary class]])) {
                
                NSArray *arrayTypeInfo = [propertyType componentsSeparatedByString:@"\""];
                if ([arrayTypeInfo count] > 2) {
                    NSString *varClassName = [arrayTypeInfo objectAtIndex:1];

                    // 创建对象
                    Class varClass = NSClassFromString(varClassName);
                    if (varClass != nil) {
                        id varObject = [[varClass alloc] init];
                        if ([varObject respondsToSelector:@selector(parseJsonObjectManual:)]) {
                            // 实现了手动解析则手动解析
                            [varObject parseJsonObjectManual:jsonValue];
                        }else{
                            [varObject parseJsonObject:jsonValue];
                        }
                        [self setValue:varObject forKey:propName];
                    }
                }
            }
        }
    }
    free(props);
}

- (NSArray *)serializableArray {
    if (![self isKindOfClass:[NSArray class]]) {
        return @[];
    }
    NSArray *arraySelf = (NSArray *)self;
    NSMutableArray *array = [[NSMutableArray alloc]initWithCapacity:arraySelf.count];
    for (id obj in arraySelf) {
        if ([obj isKindOfClass:[NSArray class]]) {
            NSArray *subArray = [obj serializableArray];
            [array addObject:subArray];
        }else{
            [array addObject:[obj serializableToDictionary]];
        }
        
    }
    return array;
}

- (id _Nullable)serializableToDictionary {
    /**
     是简单类型直接返回
     非简单类型又不是自定义Model 返回nil
     */
    if ([NSObject isSimpleValue:self]) {
        return self;
    }
    if ([NSObject isFoundationClass:[self class]]) {
        return [NSNull null];
    }
    
    NSArray<NSString *> *ignores = nil;
    NSDictionary<NSString *,NSString *> *renameMapping = nil;
    if ([self conformsToProtocol:@protocol(TJSerializableProtocol)]) {
        if ([self respondsToSelector:@selector(propertiesIgnoreInSerialize)])
            ignores = [(id<TJSerializableProtocol>)self propertiesIgnoreInSerialize];
        if ([self respondsToSelector:@selector(propertiesSerializeNameMapping)])
            renameMapping = [(id<TJSerializableProtocol>)self propertiesSerializeNameMapping];
    }
    
    NSMutableDictionary *objDictionary = [[NSMutableDictionary alloc]init];
    unsigned int count = 0;
    objc_property_t *props = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count; i++) {
        objc_property_t prop = props[i];
        
        //属性名
        const char *cName = property_getName(prop);
        NSString *propName = [NSString stringWithUTF8String:cName];
        if ([[self class] isNSObjectProperty:propName]){
            //过滤NSObject属性
            continue;
        }
        
        // 序列化乎略
        if (ignores) {
            BOOL matched = NO;
            for (NSString *ignore in ignores) {
                if ([ignore isEqualToString:propName]) {
                    matched = YES;
                    break;
                }
            }
            if (matched) continue; // go to next property.
        }
        
        // rename
        NSString *propNameSerialize = [renameMapping objectForKey:propName];
        if (!propNameSerialize) propNameSerialize = propName;
        //属性描述
//        const char *cAttr = property_getAttributes(prop);
//        NSString *propertyType = [NSString stringWithUTF8String:cAttr];
//        NSLog(@"%@", propertyType);
//        Class clazz = [self queryClassWithAttribute:propertyType];
        
        id value = [self valueForKey:propName];
        if (value) {
            //判断Value类型
            if ([[self class] isSimpleValue:value]) {
                //不处理 String Number Dictionary  直接放Dict里
            }else if ([value isKindOfClass:[NSArray class]]) {
                //递归解析数组
                value = [value serializableArray];
            }else {
                //自定义对象 递归解析
                value = [value serializableToDictionary];
            }
            [objDictionary setObject:value forKey:propNameSerialize];
        }
    }
    free(props);
    
    return objDictionary;
}

- (NSData * _Nullable)serializableToData {
    NSError *error = nil;
    NSData *jsonData = [NSJSONSerialization safe_dataWithJSONObject:[self serializableToDictionary] options:0 error: &error];
    if (!error) {
        return jsonData;
    }
    return nil;
}

- (NSString * _Nullable)serializableToString {
    NSData *jsonData = [self serializableToData];
    if (jsonData) {
        NSString *jsonStr = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
        return jsonStr;
    }
    return nil;
}


#pragma mark Private

- (NSDictionary * _Nullable)json2Dictionary {
    if ([self isKindOfClass:[NSString class]]) {
        return [NSJSONSerialization safe_JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
    }else if ([self isKindOfClass:[NSData class]]) {
        return [NSJSONSerialization safe_JSONObjectWithData:(NSData *)self options:kNilOptions error:nil];
    }else if ([self isKindOfClass:[NSDictionary class]]) {
        return (NSDictionary *)self;
    }
    return nil;
}

- (Class)queryClassWithAttribute:(NSString *)propertyAttr {
    NSString *className = nil;
    NSArray *attrSeparated = [propertyAttr componentsSeparatedByString:@"\""];
    if (attrSeparated.count > 1) {
        //对象：T@"NSDictionary",N,C,VpropertyName
        className = attrSeparated[1];
    }else{
        //基础类型：Tq,N,VpropertyName
        NSArray *normal = [propertyAttr componentsSeparatedByString:@","];
        className = normal.firstObject;
    }
    return NSClassFromString(className);
}


/**
 过滤 hash superclass description debugDescription

 @param propName propertyName
 @return 是否是NSObject属性
 */
+ (BOOL)isNSObjectProperty:(NSString *)propName {
    BOOL isNP =
    [propName isEqualToString:@"hash"] ||
    [propName isEqualToString:@"superclass"] ||
    [propName isEqualToString:@"description"] ||
    [propName isEqualToString:@"debugDescription"];
    return isNP;
    
}

+ (BOOL)isFoundationClass:(Class)clazz {
//TODO: 尝试避免穷举法...--
//    NSBundle *mainB = [NSBundle bundleForClass:clazz];
//    if (mainB == [NSBundle mainBundle]) {
//        NSLog(@"CustomClass,%@",NSStringFromClass(clazz));
//    }else{
//        NSLog(@"SystemClass,%@",NSStringFromClass(clazz));
//    }
    BOOL isFoundation =
    [clazz isSubclassOfClass:[NSString class]] ||
    [clazz isSubclassOfClass:[NSDictionary class]] ||
    [clazz isSubclassOfClass:[NSArray class]] ||
    [clazz isSubclassOfClass:[NSNumber class]] ||
    [clazz isSubclassOfClass:[NSURL class]] ||
    [clazz isSubclassOfClass:[NSDate class]] ||
    [clazz isSubclassOfClass:[NSValue class]] ||
    [clazz isSubclassOfClass:[NSData class]] ||
    [clazz isSubclassOfClass:[NSError class]] ||
    [clazz isSubclassOfClass:[NSAttributedString class]];
    
    return isFoundation;
}

+ (BOOL)isSimpleValue:(id)value {
    BOOL isSimple =
    [value isKindOfClass:[NSString class]] ||
    [value isKindOfClass:[NSNumber class]] ||
    [value isKindOfClass:[NSDictionary class]];
    return isSimple;
}

@end
